#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   EditSkills
@Time    :   2023/04/02 15:03:28
@author  :   Eval
@Version :   1.0
@Describe:   技能编辑器
'''

from PySide6.QtCore import (QMetaObject, QRect,QSize, Qt,QObject,QTimer, QUrl, Slot)
from PySide6.QtGui import (QPixmap,QCloseEvent)
from PySide6.QtWidgets import (QWidget,QLineEdit,QCheckBox,QComboBox,QPlainTextEdit,QProgressBar,QSlider)
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput,QMediaFormat
from conf.Edit.MyLael import *
from conf.Edit.MyCanvasWidget import *
from conf.GameLogin import *
from conf.Edit.SaveJson import save as JsonSave
from conf.DiaLogResult import *
from conf.Edit.EditPILMgr import *
from lib.scripts.GameEffect import *
from PIL import ImageQt
from os import listdir as OS_Listdir
import json

class EditSkillsUI(QWidget):
    def  __init__(self,form):
        super(EditSkillsUI,self).__init__()
        self.setStyleSheet("background-color:rgb(255,255,255)")
        self.form:MyWidget = form
        self.ui = SkillUI(self)
        self.setMinimumSize(QSize(600, 500))
        self.setMaximumSize(QSize(600, 500))
        self.setWindowTitle("技能编辑器")
        self.setWindowIcon(QPixmap("lib/images/title.png"))
        self.effects = {}  # 特效集
        self.render_eff:list[EffSrpite] = []  # 遍历播放特效数组 , 当特效的生命周期结束后,从这个列表删除
        self.nowPlayEff = False  # 当前是否正在播放动画

        self.skills = {}  # 技能集
        self.SkillInfo = {} # 当前选中技能的信息
        self.skillName = "" # 当前选中的技能名称
        # 存储所有加载的音效名, 音效名称:音效对象
        self.sounds:dict = {}

        self.__loadEffect("lib/images/effect")
        self.__loadSkills()
        self.ui.loadControls()

    def showUI(self):
        self.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
        self.setWindowFlags(Qt.WindowType.Widget)
        self._setAttributeData()
        
        anim = self.effects.get(self.ui.skillAnimList.currentText())
        if anim is not None:
            self.ui.selectPreView.setPixmap(anim["images"][0])
            self.ui.animName.setText(anim["effNames"][0])
            self.ui.progressBar.setValue(0)
            # 设置动画进度条长度
            self.ui.animSlider.setMaximum(len(anim["images"]))
            self.ui.AnimSLLab.setText("0")
        self.show()
    
    def closeEvent(self, event: QCloseEvent) -> None:
        # print_info("关闭技能编辑器")
        self.form.addLog("关闭技能编辑器")
        if self.ui.qTime.isActive():
            self.nowPlayEff = False
        self.ui.closeUI()
        return super().closeEvent(event)

    def _setAttributeData(self):
        # 清空子组件残留的值
        self.ui.resetUI(self)
        SkillInfo = self.skills.get(self.ui.skillArr.currentText())
        if SkillInfo is None:
            return
        self.SkillInfo = SkillInfo
        # 根据选中的图块设置编辑器的默认值,, 如果这个默认值为空,且输入框内有值, 那么就使用输入框内已经输入的值,  否则就使用 self.SkillInfo提供的数据
        def setData(control:QObject,DataKey:str):
            # 如果没有这个属性
            if self.SkillInfo.get(DataKey) is None:
                if type(control) == QLineEdit:
                    return control.text()
                elif type(control) == QPlainTextEdit:
                    return control.toPlainText()
                elif type(control) == QCheckBox:
                    return False
                elif type(control) == QComboBox:
                    return "null"
                elif type(control) == QSlider:
                    return 10
            if type(self.SkillInfo.get(DataKey)) != bool:
                return str(self.SkillInfo.get(DataKey))
            return self.SkillInfo.get(DataKey)

        self.ui.skillName.setText(self.ui.skillArr.currentText()) 
        self.ui.skillAnim.setText(setData(self.ui.skillAnim,"effect"))
        # 切换当前显示的动画为选中的技能动画名称
        self.ui.skillAnimList.setCurrentText(setData(self.ui.skillAnimList,"effect"))
        self.ui.skillAnimNum.setText(setData(self.ui.skillAnimNum,"animNum"))
        self.ui.skillTpye.setCurrentText(setData(self.ui.skillTpye,"type"))
        self.ui.skillSub.setCurrentText(setData(self.ui.skillSub,"sub"))
        self.ui.skillValue.setText(setData(self.ui.skillValue,"value"))
        self.ui.skillMP.setText(setData(self.ui.skillMP,"mp"))
        self.ui.skillCDTime.setText(setData(self.ui.skillCDTime,"cdTime"))
        self.ui.skillTarget.setText(setData(self.ui.skillTarget,"target"))
        self.ui.skillSoundName.setCurrentText(setData(self.ui.skillSoundName,"sound"))
        self.ui.skillHits.setText(setData(self.ui.skillHits,"hits"))

        self.ui.horizontalSlider.setValue(float(setData(self.ui.horizontalSlider,"volume"))*100)
        self.ui.HSLab.setText(str(self.SkillInfo.get("volume")))
        # 设置动画进度条长度
        anim = self.effects.get(self.ui.skillAnimList.currentText())
        self.ui.animSlider.setMaximum(len(anim["images"]))
        self.ui.AnimSLLab.setText("0")
        
        
    def __loadEffect(self,path):
        """私有方法 使用递归加载所有的特效文件,使用特效文件的当前目录名当作特效的名称"""
        dirArr = os.listdir(path)
        effArr = []
        effNames = []
        effName = ""
        effSize = 0
        for file in dirArr:
            if(os.path.isfile(path+"/"+file)):
                effName = path.split("/").pop()
                if file.endswith(".txt"):
                    continue
                effSize += round(int(os.path.getsize(path+"/"+file))/1024,2)
                effNames.append(file)
            else:
                self.__loadEffect(path+"/"+file)

        if not len(effNames):
            return
        # 判断配置文件是否存在
        if not os.path.exists(path+f"/{effName}.txt"):
            with open(path+f"/{effName}.txt","w+") as f:
                f.write(f"# 特效 [{effName}] 加载配置"+"\n")
                for n in effNames:
                    if n.endswith(".png") or n.endswith(".jpg"):
                        pix = QPixmap(path+"/"+n)
                        if pix.width() > 300:
                            pix = pix.scaled(300,300)
                        effArr.append(pix)
                        f.write(n+"\n")
                    else:
                        # print_error(f"不支持的格式文件:{n}")
                        self.form.addLog(f"不支持的格式文件:{n}")
        else:
            # 
            effNames.clear()
            with open(path+f"/{effName}.txt","r") as f:
                resArr = f.readlines()
                for i in resArr:
                    if i.startswith("#") or i.isspace():
                        continue
                    name = i.replace("\n","")
                    pix = QPixmap(path+"/"+name)
                    if pix.width() > 300:
                        pix = pix.scaled(300,300)
                    effArr.append(pix)
                    effNames.append(name)
                    
        self.effects[effName] = {
            "images":effArr,  # 用于存放原始的精灵,不对这个对象做修改,以及渲染
            "size":effSize,
            "effNames":effNames,
            "effPaths":path
        }
        # print_info(f"加载特效:{effName} 完成!")
        self.form.addLog(f"加载特效:{effName} 完成!")

    def __loadSkills(self):
        with open(r"lib\JsonConf\skill.json","r",encoding="utf-8") as f:
            self.skills = json.load(f)
            # 如果技能配置文件是空的, 那么需要在这里手动加一个空技能,不然无法正常添加新技能
            if not len(self.skills.keys()):
                self.skills["新技能"] = {}
            
        # 加载 sounds 目录下的所有音效
        path = "lib\sounds\\"
        files = OS_Listdir(path)
        for f in files:
            try:
                self.sounds[f] = path+f
            except:
                # print_error(f"无法识别的音效:{f}")
                self.form.addLog(f"无法识别的音效:{f}")

class SkillUI(object):
    def __init__(self,form:EditSkillsUI) -> None:
        self.form:EditSkillsUI = form
        self.ui = form.form.ui
        self.preIndex = 0
        self.pixC = None
        self.qTime = QTimer()
        self.qTime.timeout.connect(self.playPreViewAnim)
        self.qTime.start(150) 
        # 当前是否允许播放音效(播放动画时判断)
        self.isPlaySound = True

        self.player = QMediaPlayer()
        self.audioOutput = QAudioOutput()
        self.player.setAudioOutput(self.audioOutput)
        self.player.positionChanged.connect(self.positionChanged)
        self.player.durationChanged.connect(self.durationChanged)
        self.player.playbackStateChanged.connect(self.stateChanged)
        self.player.errorOccurred.connect(self._player_error)
        # Qt6中`QMediaPlayer.setVolume`已被移除，使用`QAudioOutput.setVolume`替代
        self.audioOutput.setVolume(0.1)  # 动态的根据当前技能音效设置大小

        QMetaObject.connectSlotsByName(form)

    @Slot()
    def positionChanged(self, position):
        # print_info(f'播放进度: {position}ms')
        self.form.form.addLog(f'播放进度: {position}ms')

    @Slot()
    def durationChanged(self, duration):
        # print_info(f'总时长: {duration}/ms ~= {duration/1000}s')
        self.form.form.addLog(f'总时长: {duration}/ms ~= {duration/1000}s')

    @Slot()
    def stateChanged(self, state):
        # print_info(f'状态改变: {state}')
        self.form.form.addLog(f'状态改变: {state}')

    @Slot()
    def _player_error(self, error, error_string):
        # print_info(error, error_string)
        self.form.form.addLog(f'播放音效错误 error: {error}>>>{error_string}')

    def playSound(self):
        """播放音效"""
        # print(self.form.sounds[self.skillSoundName.currentText()])
        self.audioOutput.setVolume(self.horizontalSlider.value()/100)
        self.player.setSource(QUrl.fromLocalFile(self.form.sounds[self.skillSoundName.currentText()]))
        self.player.play()

    def loadControls(self):
        self.line = QFrame(self.form)
        self.line.setGeometry(QRect(0, 40, 600, 3))
        self.line.setFrameShape(QFrame.HLine)
        self.line.setFrameShadow(QFrame.Sunken)

        self.line_2 = QFrame(self.form)
        self.line_2.setGeometry(QRect(280, 40, 3, 500))
        self.line_2.setFrameShape(QFrame.VLine)
        self.line_2.setFrameShadow(QFrame.Sunken)
        # 进度条
        self.progressBar = QProgressBar(self.form)
        self.progressBar.setObjectName("进度条")
        self.progressBar.setGeometry(QRect(465, 120, 118, 16))
        self.progressBar.setValue(0)
        self.progressBar.setTextVisible(False)
        self.progressBar.setOrientation(Qt.Horizontal)
        self.progressBar.setTextDirection(QProgressBar.TopToBottom)
        
        # 其他
        self.animLoop = self.ui.createCheckBox(290,50,labName="重复播放",form=self.form,isChecked=False)
        self.animSound = self.ui.createCheckBox(290,70,labName="播放音效",form=self.form,isChecked=False)
        self.autoUpdate = self.ui.createCheckBox(200,80,labName="自动同步",form=self.form)

        # 按钮
        self.BtnPlayAnim = self.ui.createBtnBox("播放动画",490,90,MW=75,form=self.form,event=lambda:self.playPreViewAnimFun())
        self.BtnUpdateSkill = self.ui.createBtnBox("同步动画",200,50,MW=75,form=self.form,event=lambda:self.updateSkillName(True))
        # self.BtnQuery = self.ui.createBtnBox("查看配置",220,10,MW=75,form=self.form)
        self.BtnSave = self.ui.createBtnBox("编辑",280,10,MW=75,form=self.form,event=lambda:self.saveSkill())
        self.BtnEdit = self.ui.createBtnBox("新增",370,10,MW=75,form=self.form,event=lambda:self.saveSkill("new"))
        self.BtnDelete = self.ui.createBtnBox("删除",460,10,MW=75,form=self.form,event=lambda:self.saveSkill("delete"))
        self.BtnPlaySound = self.ui.createBtnBox("播放音效",200,430,MW=75,form=self.form,event=lambda:self.playSound())
        self.BtnPILEdit = self.ui.createBtnBox("剔除不透明",480,460,MW=90,form=self.form,event= lambda: self.ChangeImgAlpha())
        # 音量滑块
        self.horizontalSlider = QSlider(self.form)
        self.horizontalSlider.setGeometry(QRect(10, 430, 160, 22))
        self.horizontalSlider.setOrientation(Qt.Horizontal)
        self.horizontalSlider.setMaximum(100)
        # 默认值
        # self.horizontalSlider.setValue(10)
        # 连接信号槽
        self.horizontalSlider.valueChanged.connect(self.__SLvaluechange)
        self.HSLab = self.ui.createLab("0",170,430,MW=25,form=self.form,style="background-color:rgba(0,0,0,0)")


        # 下拉框
        self.skillArr = self.ui.createComboBox(10,15,150,22,form=self.form,labName="技能列表",array=list(self.form.skills.keys()))
        self.skillArr.currentIndexChanged.connect(lambda: self.form._setAttributeData())
        self.skillAnimList = self.ui.createComboBox(285,90,150,22,form=self.form,labName="技能动画",array=list(self.form.effects.keys()))
        self.skillAnimList.currentIndexChanged.connect(lambda: self.updateSkillName())
        self.skillTpye = self.ui.createComboBox(20,150,150,22,form=self.form,labName="技能类型",array=["pt#普通攻击","gain#增益","Impairment#减益"])
        self.skillSub = self.ui.createComboBox(20,190,150,22,form=self.form,labName="加成类型",array=["+","-","*"])
        self.skillSoundName = self.ui.createComboBox(20,390,150,22,form=self.form,labName="音效名称",array=list(self.form.sounds.keys()))

        # 文本
        self.animName = self.ui.createLab("6",380,50,MW=200,form=self.form,style="background-color: rgba(149, 149, 149,.3);")  # 当前正在播放的动画名称
        self.ui.createLab("技能预览播放",290,120,form=self.form)
        self.ui.createLab("进度:",430,120,form=self.form)
        # 预览图
        self.selectPreView = self.ui.createLab(None,285,150,MW=300,MH=300,form=self.form,style="background-color: rgba(149, 149, 149,.3);")
        # 动画滑块
        self.animSlider = QSlider(self.form)
        self.animSlider.setGeometry(QRect(290, 460, 160, 22))
        self.animSlider.setOrientation(Qt.Horizontal)
        # 默认值
        # 连接信号槽
        self.animSlider.valueChanged.connect(self.__AnimSLvaluechange)
        self.AnimSLLab = self.ui.createLab(None,460,460,MW=25,form=self.form,style="background-color:rgba(0,0,0,0)")


        # 输入框
        self.skillName = self.ui.createLineBox(20,50,labName="技能名称",form=self.form)
        self.skillAnim = self.ui.createLineBox(20,80,labName="技能动画",form=self.form)
        self.skillAnim.setEnabled(False)
        self.skillAnimNum = self.ui.createLineBox(20,110,labName="播放次数",form=self.form)
        self.skillValue = self.ui.createLineBox(60,230,labName="值",form=self.form)
        self.skillMP = self.ui.createLineBox(20,270,labName="mp消耗",form=self.form)
        self.skillCDTime = self.ui.createLineBox(20,310,labName="冷却时间",form=self.form)
        self.skillTarget = self.ui.createLineBox(20,350,labName="打击范围",form=self.form)
        self.skillHits = self.ui.createLineBox(20,470,labName="打击次数",form=self.form)


    def __SLvaluechange(self):
        """拖动音量滑块"""
        self.HSLab.setText(str(round(self.horizontalSlider.value()/100,2)))
    
    def __AnimSLvaluechange(self):
        """拖动动画滑块"""
        anim = self.form.effects.get(self.skillAnimList.currentText())
        if anim is None:
            self.animSlider.setValue(0)
            return
        # 设置进度条
        self.preIndex = self.animSlider.value()
        # 动画最大值
        animMaxLength = len(anim["effNames"])
        proValue = self.preIndex * (100//((animMaxLength)))
        if proValue >= 100:
            proValue = 100
        if self.preIndex >= animMaxLength:
            proValue = 100
            self.preIndex = animMaxLength-1
        self.progressBar.setValue(proValue)
        self.animName.setText(anim["effNames"][self.preIndex])
        # 设置预览图
        self.selectPreView.setPixmap(anim["images"][self.preIndex])
        self.AnimSLLab.setText(str(self.animSlider.value()))

    def closeUI(self):
        """不销毁编辑器, 仅隐藏"""
        self.resetUI(self.form)
        self.preIndex = 0
        self.form.hide()

    def resetUI(self,control:QWidget):
        """清空组件残留的值"""
        childrens = control.children()
        for child in childrens:
            if type(child) == QLineEdit:
                child.setText("")
            elif type(child) == QPlainTextEdit:
                child.setPlainText("")
            # 如果这个子组件下面还有子组件, 那么执行递归清空
            if len(child.children()) > 0:
                self.resetUI(child)

    def playPreViewAnimFun(self):
        """调用预览图块的动画方法"""
        self.preIndex = 0
        self.form.nowPlayEff = not self.form.nowPlayEff

    def playPreViewAnim(self):
        """播放预览图块的动画"""
        if not self.form.nowPlayEff:
            return
        anim = self.form.effects.get(self.skillAnimList.currentText())
        if anim is None:
            return
        
        animMaxLength = len(anim["images"])
        if not animMaxLength:
            return
        
        if self.animSound.isChecked() and self.isPlaySound and self.preIndex == 0:
                # 允许播放音效,且当前索引是0, 则播放音效,不允许动画已经快执行结束了再播放音效
                self.isPlaySound = False
                self.playSound()

        self.preIndex += 1
        # 设置进度条
        proValue = (self.preIndex+1) * (round(100//(animMaxLength+1),0))
        if proValue >= 100:
            proValue = 100
        self.progressBar.setValue(proValue)

        if self.preIndex >= animMaxLength:
            self.form.nowPlayEff = False
            self.isPlaySound = True
            self.preIndex = 0
            self.progressBar.setValue(0)
            if self.animLoop.isChecked():
                self.form.nowPlayEff = True
        self.animName.setText(anim["effNames"][self.preIndex])
        # 设置预览图
        self.selectPreView.setPixmap(anim["images"][self.preIndex])

    def updateSkillName(self,update=False):
        """设置动画进度条长度,update:是否强制同步,无论有没有激活同步设置"""
        anim = self.form.effects.get(self.skillAnimList.currentText())
        self.animSlider.setMaximum(len(anim["images"]))
        self.animSlider.setValue(0)
        self.preIndex = 0
        self.AnimSLLab.setText("0")
        self.animName.setText(anim["effNames"][self.preIndex])
        # 设置预览图
        self.selectPreView.setPixmap(anim["images"][self.preIndex])
        # 如果激活了自动同步设置, 那么在右侧切换动画的时候,将自动同步到技能数据, (仅内存,需要手动保存才能持久化)
        if self.autoUpdate.isChecked() or update:
            self.skillAnim.setText(self.skillAnimList.currentText())
        # 如果切换的时候,循环播放按钮是关闭的, 那么自动调用一次播放动画的方法
        if not self.animLoop.isChecked():
            # 播放动画会反转这个值,将这里设为False
            self.form.nowPlayEff = False
            self.playPreViewAnimFun()

    def saveSkill(self,t="edit"):
        """保存资源数据"""
        skillName = self.skillName.text()
        if self.skillArr.currentText() != self.skillName.text() and t == "edit":
            # 判断当前技能列表是否有技能,没有技能时不走这
            if not len(self.form.skills.keys()):
                self.ui.ShowDiaLog_Window("编辑失败,无法编辑不存在的技能,请先点击新增按钮",form=self.form)
                return
            if self.ui.ShowDiaLog_Window("当前选中的技能名称似乎发生了更改,是否使用修改后的名称?",form=self.form) == DiaLogResult.OK:
                del self.form.skills[self.skillArr.currentText()]
                self.form.skills[skillName] = self.form.SkillInfo
                self.skillArr.clear()
                self.skillArr.addItems(list(self.form.skills.keys()))
            else:
                skillName = self.skillArr.currentText()
        elif t == "new":
            # 判断是否已经存在这个技能名称了
            if self.form.skills.get(skillName):
                self.ui.ShowDiaLog_Window("当前新增的技能名称似乎已经存在了",form=self.form)
                return
            # self.form.skills[skillName] = self.form.SkillInfo
            self.skillArr.addItem(skillName)
        
        elif t == "delete":
            # 判断要删除的技能是否存在
            if not self.form.skills.get(skillName):
                self.ui.ShowDiaLog_Window("删除失败,无法删除不存在的技能",form=self.form)
                return
            if self.ui.ShowDiaLog_Window(f"是否确认删除技能:{skillName} ?",form=self.form) == DiaLogResult.OK:
                del self.form.skills[self.skillArr.currentText()]
                if not len(self.form.skills.keys()):
                    self.form.skills["新技能"] = {}
                self.skillArr.clear()
                self.skillArr.addItems(list(self.form.skills.keys()))
                with open(r"lib\JsonConf\skill.json","w",encoding="utf8") as f:
                    f.write(JsonSave(self.form.skills))
                self.form.form.addLog(f'技能:{skillName} 删除成功!',level=1)
            return
            
        self.form.SkillInfo["effect"] = self.skillAnim.text()
        self.form.SkillInfo["animNum"] = self.skillAnimNum.text()
        self.form.SkillInfo["type"] = self.skillTpye.currentText().split("#")[0]
        self.form.SkillInfo["sub"] = self.skillSub.currentText()
        self.form.SkillInfo["value"] = self.skillValue.text()
        self.form.SkillInfo["mp"] = self.skillMP.text()
        self.form.SkillInfo["cdTime"] = self.skillCDTime.text()
        self.form.SkillInfo["target"] = self.skillTarget.text()
        self.form.SkillInfo["sound"] = self.skillSoundName.currentText()
        self.form.SkillInfo["hits"] = self.skillHits.text()
        self.form.SkillInfo["volume"] = round(self.horizontalSlider.value() / 100,2)

        self.form.skills[skillName] = self.form.SkillInfo
        self.skillArr.setCurrentText(skillName)
        # 
        with open(r"lib\JsonConf\skill.json","w",encoding="utf8") as f:
            f.write(JsonSave(self.form.skills))
        self.form.form.addLog("保存技能数据完成!")

    def ChangeImgAlpha(self):
        """修改图片为透明"""
        anim = self.form.effects.get(self.skillAnimList.currentText())
        if anim is None:
            return
        self.BtnPILEdit.setEnabled(False)
        self.BtnPILEdit.setText("处理中")
        if self.ui.ShowDiaLog_Window("即将对所有的动画精灵进行操作,此期间根据动画文件的大小会有不同的等待时间,你确定吗?",form=self.form) == DiaLogResult.OK:
            for index,name in enumerate(anim["effNames"]):
                self.BtnPILEdit.setText(f"处理中:{index}")
                img = transparent_back(anim["effPaths"]+"/"+name,self.form.form)
                pix = ImageQt.toqpixmap(img)
                if pix.width() > 300:
                    pix = pix.scaled(300,300)
                anim["images"][index] = pix
                self.form.form.addLog(f"正在处理:{name} 动画的背景")

            self.selectPreView.setPixmap(anim["images"][0])
            
        self.BtnPILEdit.setText("剔除不透明")
        self.BtnPILEdit.setEnabled(True)
        self.form.form.addLog(f"剔除动画背景完成!")